/* Copyright (c) 2021 OceanBase and/or its affiliates. All rights reserved.
miniob is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details. */

//
// Created by Wangyunlai on 2022/5/22.
//

#include "sql/stmt/update_stmt.h"
#include "sql/stmt/filter_stmt.h"
#include "sql/stmt/select_stmt.h"
#include "storage/db/db.h"
#include "storage/table/table.h"
#include <unordered_map>
#include "common/log/log.h"
#include "common/lang/string.h"
//UpdateStmt::UpdateStmt(Table *table, const Value &value, FilterStmt *filter_stmt):table_(table),filter_stmt_(filter_stmt),value_(value)
//{}
UpdateStmt::UpdateStmt(Table *table, const std::vector<UpdateStmtValue> update_values,FilterStmt* filter_stmt)
    :table_(table),update_values_(update_values),filter_stmt_(filter_stmt)
{}

UpdateStmt::~UpdateStmt()
{
  if(nullptr != filter_stmt_){
    delete filter_stmt_;
    filter_stmt_ = nullptr;
  }
}
UpdateStmtValue:: UpdateStmtValue(const UpdateStmtValue& other){
    field_=other.field_;
    value_=other.value_;
    if(other.select_stmt_!=nullptr){
    select_stmt_=new SelectStmt(*other.select_stmt_);
    }
}
UpdateStmtValue:: ~UpdateStmtValue(){
    if(select_stmt_!=nullptr){
      delete select_stmt_;
      select_stmt_=nullptr;
    }
}
UpdateStmtValue& UpdateStmtValue::operator=(const UpdateStmtValue& other){
    field_=other.field_;
    value_=other.value_;
   if(other.select_stmt_!=nullptr){
    select_stmt_=new SelectStmt(*other.select_stmt_);
    }
    return *this;
}
RC UpdateStmt::create(Db *db, const UpdateSqlNode &update_sql, Stmt *&stmt)
{
  RC rc=RC::SUCCESS;
  const char *table_name = update_sql.relation_name.c_str();
  if(nullptr ==db || nullptr == table_name){
    LOG_WARN("invalid argument. db=%p, table_name=%p", db, table_name);
    return RC::INVALID_ARGUMENT;
  }
  // Check if the table exists
  Table *table = db->find_table(table_name);
  if(nullptr == table){
    LOG_WARN("no such table. db=%s, table_name=%s", db->name(), table_name);
    return RC::SCHEMA_TABLE_NOT_EXIST;
  }

  const TableMeta &table_meta = table->table_meta();
  std::vector<UpdateInfo> update_infos=update_sql.update_infos;
  std::vector<UpdateStmtValue> update_values;
  for(UpdateInfo update_info:update_infos){
    UpdateStmtValue value;
    if(common::is_blank(update_info.attribute_name.c_str())){
      LOG_WARN("null update field");
      return RC::INVALID_ARGUMENT;
    }
    if(0==strcmp(update_info.attribute_name.c_str(),"*")){
      LOG_WARN("invalid update field *");
      return RC::INVALID_ARGUMENT;
    }
    const FieldMeta* field=table_meta.field(update_info.attribute_name.c_str());
    if(field==nullptr){
      LOG_WARN("no such field. field=%s.%s.%s", db->name(), table->name(), update_info.attribute_name);
          return RC::SCHEMA_FIELD_MISSING;
    }
    value.field_=Field(table,field);
    if(update_info.is_value){
      if(update_info.value.attr_type()!=field->type()){
        if(update_info.value.attr_type()==AttrType::NULLS&&field->can_be_null()){
          //do nothing;
        }
        else {
           bool type_cast=update_info.value.cast_to_type(field->type());
          if(!type_cast){
          LOG_WARN("field and value type mis_match");
          return RC::INVALID_ARGUMENT;
          }
        }
      }
      value.value_=update_info.value;
    }
    else {
      Stmt* sub_stmt;
      std::vector<std::string> p_tables;
      p_tables.push_back(table_name);
      rc=SelectStmt::create(db,update_info.sql,sub_stmt,p_tables);
      if(rc!=RC::SUCCESS)return rc;
      value.select_stmt_=static_cast<SelectStmt*>(sub_stmt);
    }
    update_values.push_back(value);
  }

  // Create filter statement for conditions
  std::unordered_map<std::string, Table *> table_map;
  FilterStmt *filter_stmt = nullptr;
  std::vector<Field> fields;
  table_map.insert(std::pair<std::string, Table*>(std::string(table_name), table));
   rc = FilterStmt::create(db, table,fields, &table_map,
      update_sql.conditions.data(), static_cast<int>(update_sql.conditions.size()), filter_stmt);
  if( rc != RC::SUCCESS){
    LOG_WARN("failed to create filter statement. rc=%d:%s", rc, strrc(rc));
    return rc;
  }

  std::vector<std::string> p_tables;
  p_tables.push_back(table->name());
  
  stmt = new UpdateStmt(table,update_values,filter_stmt);
  return RC::SUCCESS;
}
